package com.wsoft.bpm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wsoft.bpm.component.command.TaskSaveInstCmd;
import com.wsoft.bpm.enums.ExecutableOperation;
import com.wsoft.bpm.query.run.AddSignQuery;
import com.wsoft.bpm.query.run.AgreeQuery;
import com.wsoft.bpm.service.DynamicJumpService;
import com.wsoft.bpm.service.DynamicUserTaskService;
import com.wsoft.bpm.service.RunProcessService;
import com.wsoft.bpm.utils.ActivityUtils;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.utils.LoginUserUtil;
import lombok.extern.log4j.Log4j2;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.engine.ManagementService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.persistence.entity.TaskEntityImpl;
import org.activiti.engine.task.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

import static com.wsoft.bpm.constants.BpmConstant.*;

/**
 * @Author zhonglj
 * @Date 2024/12/5 14:44
 */
@Log4j2
@Service
public class DynamicUserTaskServiceImpl implements DynamicUserTaskService {
    @Resource
    private TaskService taskService;
    @Resource
    private DynamicJumpService dynamicJumpService;
    @Resource
    private ManagementService managementService;
    @Resource
    private RunProcessService runProcessService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addDynamicSerialNode(String originTaskId, List<String> dynamicAssignList, String taskName) {
        Task originTask = taskService.createTaskQuery().taskId(originTaskId).singleResult();
        // 创建任务
        TaskEntityImpl dynamicTask = (TaskEntityImpl) taskService.newTask();
        dynamicTask.setName(taskName);
        dynamicTask.setProcessInstanceId(originTask.getProcessInstanceId());
        dynamicTask.setProcessDefinitionId(originTask.getProcessDefinitionId());
//        dynamicTask.setExecutionId(originTask.getExecutionId());
        dynamicTask.setTaskDefinitionKey(originTask.getTaskDefinitionKey());
        dynamicTask.setCategory(DYNAMIC_SERIAL_NODE);
        dynamicTask.setAssignee(dynamicAssignList.get(0));
        dynamicTask.setParentTaskId(originTaskId);
        managementService.executeCommand(new TaskSaveInstCmd(dynamicTask));

        String jsonString = null;
        try {
            jsonString = new ObjectMapper().writeValueAsString(dynamicAssignList.subList(1, dynamicAssignList.size()));
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
            Asserts.fail("[bpm.run.error.json.parse.exception]");
        }
        // 保存审批列表到流程变量
        taskService.setVariableLocal(dynamicTask.getId(), DYNAMIC_ASSIGN_LIST, jsonString);
        taskService.setVariableLocal(dynamicTask.getId(), DYNAMIC_ORIGIN_TASK_ID, originTaskId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void serialAgree(Task task, String comment) {
        // 获取流程变量
        String originTaskId = taskService.getVariableLocal(task.getId(), DYNAMIC_ORIGIN_TASK_ID, String.class);
        if (StrUtil.isBlank(originTaskId)) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }
        Task originTask = taskService.createTaskQuery().taskId(originTaskId).singleResult();
        if (originTask == null) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }
        String jsonString = taskService.getVariableLocal(task.getId(), DYNAMIC_ASSIGN_LIST, String.class);
        List<String> dynamicAssignList = null;
        try {
            dynamicAssignList = new ObjectMapper().readValue(jsonString, new TypeReference<List<String>>() {
            });
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
            Asserts.fail("[bpm.run.error.json.parse.exception]");
        }

        // 完成任务
        if (StrUtil.isNotBlank(comment)) {
            taskService.addComment(task.getId(), originTask.getProcessInstanceId(), comment);
        }
        taskService.complete(task.getId());
        if (CollUtil.isNotEmpty(dynamicAssignList)) {
            // 继续创建动态任务执行下去
            addDynamicSerialNode(originTaskId, dynamicAssignList, task.getName());

        } else {
            // 动态上级原始节点完成，流程继续往下走
            taskService.setAssignee(originTaskId, LoginUserUtil.getUserInfo().getLoginUserVO().getId().toString());
            AgreeQuery agreeQuery = new AgreeQuery();
            agreeQuery.setTaskId(originTaskId);
            agreeQuery.setComment(null);
            runProcessService.agree(agreeQuery);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void serialRefuse(Task task, String comment) {
        String originTaskId = taskService.getVariableLocal(task.getId(), DYNAMIC_ORIGIN_TASK_ID, String.class);
        if (StrUtil.isBlank(originTaskId)) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }
        Task originTask = taskService.createTaskQuery().taskId(originTaskId).singleResult();
        if (originTask == null) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }

        // 完成任务
        if (StrUtil.isNotBlank(comment)) {
            taskService.addComment(task.getId(), originTask.getProcessInstanceId(), comment);
        }
        taskService.complete(task.getId());
        // 获取上一个审批节点
        List<FlowElement> preFirstUserTask = ActivityUtils.getPreFirstUserTask(originTaskId);
        if (CollUtil.isEmpty(preFirstUserTask)) {
            Asserts.fail("[bpm.dynamic.preUser.empty]");
        }
        String originPreNode = preFirstUserTask.get(0).getId();
        // 跳转上一个节点
        dynamicJumpService.outgoingJump(originTaskId, CollUtil.newArrayList(originPreNode), null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addDynamicParallelNode(String originTaskId, String sourceTaskId, List<String> dynamicAssignList, String type, String taskName) {
        Task originTask = taskService.createTaskQuery().taskId(originTaskId).singleResult();
        for (String assign : dynamicAssignList) {
            // 创建任务
            TaskEntityImpl dynamicTask = (TaskEntityImpl) taskService.newTask();
            dynamicTask.setProcessInstanceId(originTask.getProcessInstanceId());
            dynamicTask.setProcessDefinitionId(originTask.getProcessDefinitionId());
//            dynamicTask.setExecutionId(originTask.getExecutionId());
            dynamicTask.setTaskDefinitionKey(originTask.getTaskDefinitionKey());
            dynamicTask.setName(taskName);
            dynamicTask.setCategory(DYNAMIC_PARALLEL_NODE);
            dynamicTask.setAssignee(assign);
            dynamicTask.setParentTaskId(sourceTaskId);
            managementService.executeCommand(new TaskSaveInstCmd(dynamicTask));
        }

        // 临时变量
        taskService.setVariableLocal(sourceTaskId, DYNAMIC_ORIGIN_TASK_ID, originTaskId);
        taskService.setVariableLocal(sourceTaskId, DYNAMIC_SIGN_TYPE, type);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void parallelAgree(Task task, String comment) {
        String originTaskId = taskService.getVariableLocal(task.getParentTaskId(), DYNAMIC_ORIGIN_TASK_ID, String.class);
        if (StrUtil.isBlank(originTaskId)) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }
        Task originTask = taskService.createTaskQuery().taskId(originTaskId).singleResult();
        if (originTask == null) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }

        // 完成任务
        if (StrUtil.isNotBlank(comment)) {
            taskService.addComment(task.getId(), originTask.getProcessInstanceId(), comment);
            taskService.complete(task.getId());
        } else {
            // 由于子任务引起的递归同意,中间同意不需要记录信息，只需要将任务结束即可
            taskService.complete(task.getId());
        }


        String sourceTaskId = task.getParentTaskId();
        if (StrUtil.isBlank(sourceTaskId)) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }
        if (isAllCompleteUnderSource(sourceTaskId)) {
            // 并行任务执行结束，判断加签类型
            String type = taskService.getVariableLocal(sourceTaskId, DYNAMIC_SIGN_TYPE, String.class);
            if (StrUtil.equals(ExecutableOperation.SIGN_THEN_APPROVAL.getValue(), type)) {
                // 加签人先审,来源节点审批
                String owner = taskService.getVariableLocal(sourceTaskId, DYNAMIC_ASSIGN_OWNER, String.class);
                taskService.setAssignee(sourceTaskId, owner);

            } else if (StrUtil.equals(ExecutableOperation.APPROVAL_THEN_SIGN.getValue(), type)) {
                // 加签人后审,递归同意
                String owner = taskService.getVariableLocal(sourceTaskId, DYNAMIC_ASSIGN_OWNER, String.class);
                taskService.setAssignee(sourceTaskId, owner);
                AgreeQuery agreeQuery = new AgreeQuery();
                agreeQuery.setTaskId(sourceTaskId);
                agreeQuery.setComment(null);
                agreeQuery.setAuthCheck(false);
                // 获取加签人后审保留的请求数据
                String queryDataStr = taskService.getVariableLocal(sourceTaskId, DYNAMIC_ASSIGN_QUERY_DATA, String.class);
                if (StrUtil.isNotBlank(queryDataStr)) {
                    AddSignQuery signQuery = BeanUtil.toBean(JSONUtil.parseObj(queryDataStr), AddSignQuery.class);
                    agreeQuery.setTaskExecute(signQuery.getTaskExecute());
                    agreeQuery.setTaskCandidate(signQuery.getTaskCandidate());
                    agreeQuery.setNextActIds(signQuery.getNextActIds());
                }
                runProcessService.agree(agreeQuery);

            } else {
                Asserts.fail("[bpm.run.error.execute.opt.unknown]");
            }
        }
    }

    /**
     * 来源任务下所有并行子任务都完成
     *
     * @param sourceTaskId
     * @return boolean
     * @author zhonglj
     * @date 2024/12/7 13:50
     **/
    private boolean isAllCompleteUnderSource(String sourceTaskId) {
        List<Task> collect = taskService.createTaskQuery()
                .taskParentTaskId(sourceTaskId)
                .active()
                .list()
                .stream()
                .collect(Collectors.toList());

        return CollUtil.isEmpty(collect);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void parallelRefuse(Task task, String comment) {
        String originTaskId = taskService.getVariableLocal(task.getParentTaskId(), DYNAMIC_ORIGIN_TASK_ID, String.class);
        if (StrUtil.isBlank(originTaskId)) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }
        Task originTask = taskService.createTaskQuery().taskId(originTaskId).singleResult();
        if (originTask == null) {
            Asserts.fail("[bpm.run.error.node.fetch]");
        }

        // 完成任务
        if (StrUtil.isNotBlank(comment)) {
            taskService.addComment(task.getId(), originTask.getProcessInstanceId(), comment);
            taskService.complete(task.getId());

        } else {
            // 由于子任务引起的递归退回,中间退回不需要记录信息，只需要将任务结束即可
            taskService.complete(task.getId());
        }

        String sourceTaskId = task.getParentTaskId();
        String type = taskService.getVariableLocal(sourceTaskId, DYNAMIC_SIGN_TYPE, String.class);
        if (StrUtil.equals(ExecutableOperation.SIGN_THEN_APPROVAL.getValue(), type)) {
            // 加签人先审，加签节点还没开始审批，递归拒绝
            taskService.setAssignee(sourceTaskId, LoginUserUtil.getUserInfo().getLoginUserVO().getId().toString());
            runProcessService.refuse(sourceTaskId, null, false);

        } else if (StrUtil.equals(ExecutableOperation.APPROVAL_THEN_SIGN.getValue(), type)) {
            // 加签人后审，加签节点已经审批通过，退回加签人节点
            String owner = taskService.getVariableLocal(sourceTaskId, DYNAMIC_ASSIGN_OWNER, String.class);
            taskService.setAssignee(sourceTaskId, owner);

            // 完结掉sourceTaskId下的子任务
            taskService.createTaskQuery()
                    .taskParentTaskId(sourceTaskId)
                    .active()
                    .list()
                    .stream()
                    .forEach(subTask -> taskService.complete(subTask.getId()));
        } else {
            Asserts.fail("[bpm.run.error.execute.opt.unknown]");
        }

    }
}
